#ifndef ELEMENT_H
#define ELEMENT_H

#include "AReVi/Lib3D/base3D.h"
namespace AReVi {
	class Object3D;
	class Activity;
}
using namespace AReVi;

class Event;

/** An element constituing the environment */
class Element : public ArObject {
public:
    AR_CLASS(Element)
    friend class Environment;
    
	/** Retreive ID class.index@client */
	inline StlString getID() const { return _id; }
    /** Retreive class */
    inline StlString getClassName() const { return _id_classname; }
    /** Retreive index */
    inline StlString getIndex() const { return _id_index; }
    /** Retreive client */
    inline StlString getClient() const { return _id_client; }

    /** Local or global Element */
    inline bool isLocal(void) const { return _local; }

    /** Initialise element. Must be done after calling constructor.
     *  Can be used to differenciate local and distant elements */
    virtual void init() = 0;

	/** Retreive representation */
	virtual ArRef<Object3D> getRepresentation() const = 0;

	/** Retreive linear acceleration */
	void getLinearAcc(double & x, double & y, double & z);
	/** Set linear acceleration */
	void setLinearAcc(double x, double y, double z);
	/** Retreive linear speed */
	void getLinearSpeed(double & x, double & y, double & z);
	/** Set linear speed */
	void setLinearSpeed(double x, double y, double z);

	/** Retreive linear speed limits */
	void getLinearSpeedLimit(double & xmin, double & ymin, double & zmin, double & xmax, double & ymax, double & zmax);
	/** Set linear speed limits */
	void setLinearSpeedLimit(double xmin, double ymin, double zmin, double xmax, double ymax, double zmax);

	/** Retreive angular speed */
	void getAngularAcc(double & roll, double & pitch, double & yaw);
	/** Set angular speed */
	void setAngularAcc(double roll, double pitch, double yaw);
	/** Retreive angular speed */
	void getAngularSpeed(double & roll, double & pitch, double & yaw);
	/** Set angular speed */
	void setAngularSpeed(double roll, double pitch, double yaw);

	/** Retreive angular speed limits */
	void getAngularSpeedLimit(double & xmin, double & ymin, double & zmin, double & xmax, double & ymax, double & zmax);
	/** Set linear speed limits */
	void setAngularSpeedLimit(double xmin, double ymin, double zmin, double xmax, double ymax, double zmax);

    /** Retreive representation's position */
    void getPosition(double & pxOut, double & pyOut, double & pzOut) const;
    /** Apply translation on the element */
    void translate(double dx, double dy, double dz);
    /** Set representation's position */
    void setPosition(double px, double py, double pz);
    
    /** Retreive representation's orientation */
    void extractOrientation(double & rollOut, double & pitchOut, double & yawOut) const;
    /** Rotation operations */
    void roll(double dRoll);
    void pitch(double dPitch);
    void yaw(double dYaw);
    /** Set representation's orientation */
    void setOrientation(double roll, double pitch, double yaw);

protected:
    /** Abstract class -> protected constructor 
     *  Unable to differenciate local and distant object 
     *  Local by default */
    Element(ArCW & arCW);

    /** Fire a creation Event */
    void fireCreate();
    /** Fire an info Event (requested create) */
    void fireInfo();
    /** Fire an update Event */
    void fireUpdate();
    /** Fire a location Event */
    void fireUpdateLocation();
    /** Fire deletion Event */
    void fireDelete();
    
    /** Create Element from event */
    static ArRef<Element> createFromEvent(ArRef<Event> evt);

    /** Callback to manage updates */
    void _onUpdate(ArRef<Event> evt);

	/** Callback to manage location updates */
	void _onUpdateLocation(ArRef<Event> evt);

    /** Write Element configuration to stream */
    virtual void writeConfiguration(ArRef<AbstractOStream> oStream) = 0;  
    /** Read Element configuration to stream */
    virtual void readConfiguration(ArRef<AbstractIStream> iStream) = 0;

    /** Write Element configuration to stream */
    virtual void writeLocation(ArRef<AbstractOStream> oStream);
    /** Read Element configuration to stream */
    virtual void readLocation(ArRef<AbstractIStream> iStream);

    /** Write Element configuration to stream */
    virtual void writeUpdate(ArRef<AbstractOStream> oStream) = 0;  
    /** Read Element configuration to stream */
    virtual void readUpdate(ArRef<AbstractIStream> iStream) = 0;

    /** To update ID */
    void updateID();
    /** To parse ID */
    static void parseID(const StlString & id, StlString & classname, StlString & index, StlString & client);
    
    /** Complete ID */
	StlString _id;
	/** Class Name */
	StlString _id_classname;
	/** Index */
	StlString _id_index;
	/** Client */
	StlString _id_client;
	/** Is element local or not ? */
    bool _local;
    
    /** Activity updating location (ref) */
    ArRef<Activity> _actUpdateLocation;
    /** Activity updating location (method) */
    bool _updateLocation(ArRef<Activity> act, double dt);
    
    /** To update location */
    virtual void updateLocation();

	/** Last time location was updated */
    double _lastLocationUpdate;
    
    /** Speed and acceleration (linear) */
    Util3D::Dbl3 _linearSpeed, _linearAcc, _linearSpeedMin, _linearSpeedMax;
    /** Speed and acceleration (angular) */
    Util3D::Dbl3 _angularSpeed, _angularAcc, _angularSpeedMin, _angularSpeedMax;
};

#endif
